package client;

import info.clearthought.layout.TableLayout;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;

import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPasswordField;
import javax.swing.JTextField;

import utils.Settings;

import commands.CommandNewUser;
import commands.CommandUserLogon;
import components.RPSButton;
import components.RPSPanel;

/**
 * This panel is the logon panel for the user
 */
public class LogonPanel extends RPSPanel { 

	private static final long serialVersionUID = 1L;
	
	private static final String DEFAULT_PORT   = String.valueOf(Settings.SERVER_PORT);
	private static final String DEFAULT_SERVER = Settings.SERVER_HOST;
	
	private static final int NUM_OF_COLUMNS = 11;
	
	private JTextField usernameTextField;
	private JTextField serverTextField, portTextField;
	private JPasswordField passwordTextField;
	private RPSButton logonButton, exitButton, newUserButton;    
	private ClientFrame clientFrame;

	private HashMap<JTextField, Integer> mapJTextFieldToMaximalLength = new HashMap<JTextField, Integer>();
	private ConnectionThreadRPSClientToServer conn = null;
	
	
	public LogonPanel (ClientFrame clientFrame) {
		this.clientFrame = clientFrame;
		setUpGUI ();
		addListeners ();
	}

	private void setUpGUI () {
		
		double pref = TableLayout.PREFERRED;
		double fill = TableLayout.FILL;
		double[][] sizes = {{fill}, {fill}};
		
		setLayout (new TableLayout (sizes));

		// Create main panel
		sizes = new double[][] {{fill}, {pref, 25, pref, 15}};
		RPSPanel mainPanel = new RPSPanel();
		mainPanel.setLayout(new TableLayout (sizes));
		mainPanel.add (getLogonPanel (),  "0,0,c,c");
		mainPanel.add (getButtonPanel (), "0,2,c,c");
		
		add (mainPanel, "0,0,c,c");
	}

	private void addListeners () {
		// Add button listeners
		logonButton.addActionListener (new ActionListener() {
			public void actionPerformed (ActionEvent event) {
				if (validateInput()) {	
					try {
						// Disable the okButton so the user can't press more times
						setButtons(false);

						connect ();
					} catch (UnknownHostException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		});
	
		newUserButton.addActionListener (new ActionListener() {
			public void actionPerformed (ActionEvent event) {
					
				try {
					// Disable the okButton so the user can't press more times
					setButtons(false);
					addToDb();
				} catch (Exception e) {
					e.printStackTrace();
				}
				
			}
		});
	
		exitButton.addActionListener (new ActionListener () {
			public void actionPerformed (ActionEvent event) {
				closeClient();
			}
		});
		
		KeyListener keyListener = new KeyListener() {
			@Override
			public void keyTyped(KeyEvent e)
			{
				char chr = e.getKeyChar();
				JTextField textFieldSource = (JTextField) e.getSource();
				int currTextLength = textFieldSource.getText().length();
				
				if (chr == KeyEvent.VK_ENTER)
				{
					logonButton.doClick();
				}
				
				// If we've reached the maximal length or the key is NOT a special one,
				// stop dealing with this key event (Otherwise, the character will be added
				// 									 normally to the JTextField)
				if (mapJTextFieldToMaximalLength.containsKey(textFieldSource) &&
					currTextLength >= mapJTextFieldToMaximalLength.get(textFieldSource))
				{
					if(! (chr == KeyEvent.VK_BACK_SPACE || chr == KeyEvent.VK_DELETE ||
						  chr == KeyEvent.VK_TAB        || e.isActionKey()))
					{
						e.consume(); 
					}
				}				
			}
			
			@Override
			public void keyReleased(KeyEvent e)
			{
			}	
			@Override
			public void keyPressed(KeyEvent e)
			{
			}
		};
		
		// Set maximum lengths
		usernameTextField.addKeyListener(keyListener);
		passwordTextField.addKeyListener(keyListener);
		serverTextField.addKeyListener(keyListener);
		portTextField.addKeyListener(keyListener);
	}
	
	private boolean validateInput() {
		String messageWhatToFix = "";
		String newLine = Settings.NEW_LINE;
		boolean isValid = true;
		
		// Username and Password
		if (usernameTextField.getText().isEmpty())
		{
			messageWhatToFix += "Username field cannot be empty" + newLine;
		}

		// Server and port
		if (serverTextField.getText().isEmpty())
		{
			messageWhatToFix += "Server field cannot be empty" + newLine;
		}
		
		try {
			Integer.parseInt(portTextField.getText().trim());
		} catch (NumberFormatException e) {
			messageWhatToFix += "Port field must be an Integer" + newLine;
		}
		
		// Remove the last new line and show a message box to the user
		if (messageWhatToFix.isEmpty() == false)
		{
			isValid = false;
			// Remove the new-line at the end of the last line from the validation message
			messageWhatToFix = messageWhatToFix.substring(0, messageWhatToFix.length() - newLine.length());
			JOptionPane.showMessageDialog(clientFrame, messageWhatToFix, "Input Validation", JOptionPane.ERROR_MESSAGE);
		}
		
		return isValid;
	}
	
	private RPSPanel getLogonPanel () {
		// Create login panel
		double pref = TableLayout.PREFERRED, space = 5;
		double [][] sizes = new double [][] {{space, pref, space, pref, space},
				{space, pref, space, pref, space, pref, space, pref, space, pref}};
		RPSPanel panel = new RPSPanel(new TableLayout (sizes));

		// Create widgets
		JLabel usernameLabel = new JLabel ("Username" + ":");
		JLabel passwordLabel = new JLabel ("Password" + ":");
		JLabel serverLabel   = new JLabel ("Server" + ":");
		JLabel portLabel     = new JLabel ("Port" + ":");
		usernameTextField = new JTextField (NUM_OF_COLUMNS);
		passwordTextField = new JPasswordField (NUM_OF_COLUMNS);
		serverTextField   = new JTextField (DEFAULT_SERVER, NUM_OF_COLUMNS);
		portTextField     = new JTextField (DEFAULT_PORT, 4);

		// Assign maximal length to some JTextFields
		mapJTextFieldToMaximalLength.put(serverTextField, 15);
		mapJTextFieldToMaximalLength.put(portTextField, 5);
		
		// Add items to logon panel
		panel.add (usernameLabel,     "1,1,r,c");
		panel.add (passwordLabel,     "1,3,r,c");
		panel.add (serverLabel,       "1,5,r,c");
		panel.add (portLabel,         "1,7,r,c");
		panel.add (usernameTextField, "3,1,l,c");
		panel.add (passwordTextField, "3,3,l,c");
		panel.add (serverTextField,   "3,5,l,c");
		panel.add (portTextField,     "3,7,l,c");

		// Return logon panel
		return panel;
	}

	private RPSPanel getButtonPanel () {
		// Create panel
		double pref = TableLayout.PREFERRED, space = 80;
		double [][] sizes = new double [][] {{pref, space, pref,}, {pref}};
		RPSPanel buttonPanel = new RPSPanel (new TableLayout (sizes));

		// Create buttons
		logonButton = new RPSButton ("Logon");
		exitButton = new RPSButton ("Exit");
		newUserButton = new RPSButton ("NewUser");
		
		buttonPanel.add (logonButton, "0,0");
		buttonPanel.add (exitButton, "2,0");
		buttonPanel.add (newUserButton, "1,0");
		
		return buttonPanel;
	}
	
	public void connect () throws UnknownHostException, IOException {
		try {
			String  server, username, password;
			int     port;
	
			// Retrieve server and port number
			port     = Integer.parseInt (portTextField.getText().trim());
			server   = serverTextField.getText();
			username = usernameTextField.getText();
			password = new String(passwordTextField.getPassword());
			
			// Create a socket connection to the RPS server
			Socket socket = new Socket (server, port);
			conn = new ConnectionThreadRPSClientToServer (socket, username, this, this.clientFrame);
			conn.start();
			
			// Send connect message to the server.
			CommandUserLogon commandUserLogon = new CommandUserLogon(username, password);	
			conn.send (commandUserLogon);
		}
		catch (Exception ex) {
			JOptionPane.showMessageDialog(clientFrame, "Uable To connect. Please try again later.", "Connection Problems", JOptionPane.ERROR_MESSAGE);
			setButtons(true);
		}
	}
	
	public void addToDb() {
		try {	
			String  server, username, password;
			int port;
			
			// Retrieve server and port number
			port     = Integer.parseInt (portTextField.getText().trim());
			server   = serverTextField.getText();
			username = usernameTextField.getText();
			password = new String(passwordTextField.getPassword());
			
			// Create a socket connection to the RPS server
			Socket socket = new Socket (server, port);
			conn = new ConnectionThreadRPSClientToServer (socket, username, this, this.clientFrame);
			conn.start();
			
			// Send connect message to the server.
			CommandNewUser commandNewUser = new CommandNewUser(username, password);
			conn.send (commandNewUser);
		}
		catch (Exception ex) {
			JOptionPane.showMessageDialog(clientFrame, "Unable To connect. Please check your connection details.", "Connection Problems", JOptionPane.ERROR_MESSAGE); 
			setButtons(true);
		}
	}
	
	public void setButtons(boolean value) {
		logonButton.setEnabled(value);
		exitButton.setEnabled(value);
		newUserButton.setEnabled(value);
	}
	
	public void closeClient()
	{
		System.exit(0);
	}
}